﻿
Imports System.Threading
Imports std
Imports storoomc

Public MustInherit Class uploader(Of T)
    Public Shared defaultStoroomHost As String = Nothing
    Public Shared defaultStoroomPort As Int32 = 0
    Public Const dpslibUploaderHostlist As String = "dpslib.uploader.hostlist"
    Protected talk As storoomTalk = Nothing
    Private queue As queue(Of T) = Nothing
    Private lock As sequentialLock = Nothing
    Private thread As Thread = Nothing

    Shared Sub New()
        If isdebugmode() Then
            defaultStoroomHost = "localhost"
            defaultStoroomPort = 42371
        Else
            defaultStoroomHost = "hzj-server-1"
            defaultStoroomPort = 42442
        End If
    End Sub

    Protected MustOverride Sub addWriteEventHandler()
    Protected MustOverride Sub removeWriteEventHandler()

    Private Function initialStoroomTalk(ByVal storoom As pair(Of String, Int32)) As Boolean
        If talk Is Nothing Then
            talk = New storoomTalk()
            talk.liveTime = storoomc.common.talkTime.client2room.livetime << 3
        End If
        talk.initial(storoom.first, storoom.second, 1)

        'get dpslib upload hostlist from storoom directly to decrease the cost of changing it
        Dim buff As bytes = Nothing
        talk.holdTime = 10000
        If directorystore.readfile(configurationsStoroomDirectory, dpslibUploaderHostlist, buff, talk) Then
            Dim s As String = Nothing
            s = encoding.utf8().GetString(+buff)
            Dim v As vector(Of String) = Nothing
            strsepar(s, character.newline, v, True)
            If Not v Is Nothing AndAlso v.size() > 0 Then
                s = v(rnd(0, v.size(), True))
                kickLeftRightBlank(s)
                If parseHostPort(s, storoom.first, storoom.second) Then
                    talk.initial(storoom.first, storoom.second, 1)
                End If
            End If
        Else
            raiseError("no ", dpslibUploaderHostlist, " found in storoom, ignore")
        End If
        talk.holdTime = storoomc.common.talkTime.client2room.holdtime

        Return True
    End Function

    Public Function initial(ByVal storoom As pair(Of String, Int32)) As Boolean
        If storoom Is Nothing Then
            Return False
        Else
            If initialStoroomTalk(storoom) Then
                assert((lock Is Nothing) = (queue Is Nothing))
                assert((lock Is Nothing) = (thread Is Nothing))
                If lock Is Nothing Then
                    lock = New sequentialLock()
                    queue = New queue(Of T)()
                    addWriteEventHandler()
                    AddHandler applicationShutdown, AddressOf applicationShutdownHandler
                    thread = startThread(AddressOf uploadThread)
                End If
                Return True
            Else
                Return False
            End If
        End If
    End Function

    Public Function initial() As Boolean
        Return initial(make_pair(defaultStoroomHost, defaultStoroomPort))
    End Function

    Private Sub uploadThread()
begin:
        Try
            While True
                If Not upload() Then
                    sleep()
                End If
            End While
        Catch ex As ThreadAbortException
            raiseError("thread aborted.")
        Catch ex As Exception
            raiseError(errorHandle.errorType.critical, _
                       "unhandled exception caught in uploadCounterThread, ex ", ex.Message, _
                       ", call stack ", ex.StackTrace, ", uploadCounterThread restarted.")
            GoTo begin
        End Try
    End Sub

    Protected MustOverride Function upload(ByVal v As T) As Boolean

    Private Function upload() As Boolean
        assert(Not talk Is Nothing)
        assert(Not lock Is Nothing)
        If queue.empty() Then
            Return False
        Else
            'do not need to lock when upload, since there is always only one upload thread
            If upload(queue.front()) Then
                lock.wait()
                queue.pop()
                lock.release()
            Else
                raiseError(errorHandle.errorType.warning, _
                           "cannot write data to storoom, wait for next round.")
            End If

            Return True
        End If
    End Function

    Protected Sub push_queue(ByVal i As T)
        assert(Not queue Is Nothing)
        assert(Not lock Is Nothing)
        lock.wait()
        queue.push(i)
        lock.release()
    End Sub

    Private Sub applicationShutdownHandler()
        removeWriteEventHandler()
        assert(Not thread Is Nothing)
        thread.Abort()
        thread.Join()

#If CANNOT_DETERMINE Then
        Dim startticks As Int64
        startticks = Now.Ticks()
        Static waitticks As Int64 = secondsToTicks(10)
        While upload() AndAlso Now.Ticks() - startticks < waitticks
        End While
#End If
    End Sub
End Class